#include "inneral/xthread.h"
#include "inneral/xthread-timer.h"

#include <assert.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

static LIST_HEAD(xtimer_list_head);
static unsigned long xtimer_id_next = 1; /* 从1开始，0是无效的id */
static volatile clock_t min_timeout_val = 0;
static void xtimer_idle_handler(void *arg);
static xthread_timer_t xtimer_idle;
static xthread_spin_lock_t xtimer_lock;
volatile int __xtimer_handle_failed = 0;

/* idle定时器超时
 * 重新调整所有定时器的值，所有值都减去xthread_timer_ticks（idle除外）
 */
static void xtimer_idle_handler(void *arg)
{
    clock_t dt = xtimer_idle.timeout;
    __xthread_alarm_ticks -= dt;
    xthread_timer_t *tmp;
    xthread_spin_lock(&xtimer_lock);
    list_for_each_owner (tmp, &xtimer_list_head, list) {
        if (tmp != &xtimer_idle)
            tmp->timeout -= dt;
    }
    xthread_spin_unlock(&xtimer_lock);
    xthread_timer_add(&xtimer_idle);
}

void xthread_timer_set(
    xthread_timer_t *xtimer,
    unsigned long timeout,
    xthread_timer_callback_t callback,
    void *arg)
{
    list_init(&xtimer->list);
    xtimer->timeout = __xthread_alarm_ticks + timeout;
    xtimer->arg = arg;
    xtimer->id = xtimer_id_next++;
    xtimer->callback = callback;
    xthread_spin_init(&xtimer->lock);
}

int xthread_timer_add(xthread_timer_t *xtimer)
{
    if (!__xthread_init_done)
        return -1;
    xthread_spin_lock(&xtimer_lock);
    if (!xtimer->id)
        xtimer->id = xtimer_id_next++;
    assert(!list_find(&xtimer->list, &xtimer_list_head));
    if (list_empty(&xtimer_list_head)) {    
        list_add_tail(&xtimer->list, &xtimer_list_head);
        min_timeout_val = xtimer->timeout;
    } else {
        xthread_timer_t *first = list_first_owner(&xtimer_list_head, xthread_timer_t, list);
        if (xtimer->timeout < first->timeout) {
            list_add(&xtimer->list, &xtimer_list_head);
            min_timeout_val = xtimer->timeout;
        } else {
            xthread_timer_t *tmp;
            list_for_each_owner (tmp, &xtimer_list_head, list) {
                if (xtimer->timeout >= tmp->timeout) {
                    list_add_after(&xtimer->list, &tmp->list);
                    break;
                }
            }
        }
    }
    xthread_spin_unlock(&xtimer_lock);
    return 0;
}

void xthread_timer_del(xthread_timer_t *xtimer)
{
    if (!__xthread_init_done)
        return;
    xthread_spin_lock(&xtimer_lock);
    assert(list_find(&xtimer->list, &xtimer_list_head));
    list_del(&xtimer->list);
    xthread_spin_unlock(&xtimer_lock);
}

int xthread_timer_alive(xthread_timer_t *xtimer)
{
    int alive = 0; 
    xthread_spin_lock(&xtimer_lock);
    if (list_find(&xtimer->list, &xtimer_list_head))
        alive = 1;
    xthread_spin_unlock(&xtimer_lock);
    return alive;
}

void xthread_timer_modify(xthread_timer_t *xtimer, unsigned long timeout)
{
    xthread_spin_lock(&xtimer->lock);
    xtimer->timeout = __xthread_alarm_ticks + timeout;
    xthread_spin_unlock(&xtimer->lock);
}

int xthread_timer_cancel(xthread_timer_t *xtimer)
{
    if (!__xthread_init_done)
        return -1;
    int retval = -1;
    if (xtimer) {
        if (xthread_timer_alive(xtimer)) {
            xthread_timer_del(xtimer);
        }
        retval = 0;
    }
    return retval;
}

static void xtimer_handle(xthread_timer_t *xtimer)
{
    xtimer->callback(xtimer->arg);
}

void xthread_timer_update()
{
    if (!__xthread_init_done)
        return;
    xthread_timer_t *xtimer, *next;
    if(xthread_spin_trylock(&xtimer_lock))
        return;

    if (__xthread_alarm_ticks < min_timeout_val) { // no xtimer timeout
        xthread_spin_unlock(&xtimer_lock);
        return;
    }
    list_for_each_owner_safe (xtimer, next, &xtimer_list_head, list) {
        if (xtimer->timeout > __xthread_alarm_ticks) {
            break;
        }
        __xtimer_handle_failed = 0;
        xtimer_handle(xtimer); // time out
        if (__xtimer_handle_failed) {
            /* handle failed! just return, for next time update timer */
            xthread_spin_unlock(&xtimer_lock);
            return;
        }
        list_del(&xtimer->list);
    }
    min_timeout_val = xtimer->timeout;
    xthread_spin_unlock(&xtimer_lock);
}

xthread_timer_t *xthread_timer_create(unsigned long timeout, xthread_timer_callback_t callback, void *arg)
{
    if (!callback)
        return NULL;
    xthread_timer_t *timer = malloc(sizeof(xthread_timer_t));
    if (!timer)
        return NULL;
    xthread_timer_set(timer, timeout, callback, arg);
    return timer;
}

int xthread_timer_destroy(xthread_timer_t *timer)
{
    if (!timer)
        return -EINVAL;
    if (xthread_timer_cancel(timer) < 0)
        return -EPERM;
    free(timer);
    return 0;
}

int xthread_timer_init()
{
    xthread_spin_init(&xtimer_lock);
    xthread_timer_set(&xtimer_idle, XTIMER_IDLE_TIMEOUT, xtimer_idle_handler, NULL);
    xthread_timer_add(&xtimer_idle);
    return 0;
}